#include "mat_mapstate.h"
#include "mat_game_editor.h"
#include "mat_define.h"
#include "gen_game_state.h"
#include "mat_level_manager.h"
#include "mat_ui_popupanimator.h"
#include "mat_ui_animator.h"
#include "gameflow/game_flow_manager.h"
#include "mat_sounds.h"
#include "mat_sfx_manager.h"
#include "build.h"

#include "audio/audio.h"
#include "application/game_rms.h"
#include "background/bg_scene.h"
#include "background/bg_waypoint.h"
#include "background/bg_decal.h"
#include "application/game_application.h"
#include "script/scripthelper.h"
#include "script/write_lua.h"

#include "dd/dd_man.h"
#include "ui/ui_manager.h"
#include "gameflow/game_flow_manager.h"
#include "num/rand.h"
#include "graphics/graphics_util.h"
#include "math/facing.h"

#include "font/font_manager.h"

#include "util/timer.h"				// log
#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    

#ifdef _ENABLE_LUABIND
#include <luabind/luabind.hpp>
#endif

#include <cassert>

using namespace script;


const float			MATMapState::WP_SIZE =  0.06f;

MATMapState::MATMapState(camera::FbCamera*	pCamera, const std::string& File) :
	gameflow::UIState(pCamera, File, "",""),
	m_IsMouseUp(true),
	m_CurrentWP(1),
	m_CanMove(true),
	m_Delay(0),
	m_DelayGotoGame(0),
	m_ToSpawnParticle(false),
	m_IsDebug(false),
	m_AnimatedLevelIndex(-1),
	m_IsFinishAddingGameState(false),
	m_IsLoaded(false)
{	
	
	m_Music = "sounds/theme_menu.ogg";
	
}

void	MATMapState::LoadAsset()
{
	m_Part2DIamHere.Load("effects/levelup.p2d");	
	m_CounterParticleRespawn.SetLimit(0.35f);
	m_CounterParticleRespawn.ForceReady();

	m_Scale = (float)GraphicsUtil::WIDTH / (float)TEXTURE_WIDTH;

	char	Buffer[256];
	
	for(int i = 0; i < 3; i++)
	{
		sprintf(Buffer, "star_map_%i", i+1);
		m_Stars[i].Create(Buffer);
		m_StarSize = m_Stars[i].m_Size * m_Scale;
	}

	m_SprLockedLevel.Create("level_locked");
	m_SprLockedLevel.m_Color = ColorfRGBA(1.0f,1.0f,1.0f,0.75f);
	m_SprLockedLevel.m_Size *= m_Scale;

	
	
	
	//const float AspectRatioCreated = 720.0f / 1280.0f;
	//const float AsepctRatioCurrent = (float)GetGraphics()->GetScreenWidth() / (float)GetGraphics()->GetScreenHeight();
	//const float NewWScale =  AspectRatioCreated / AsepctRatioCurrent;	
	

	//SetBGScale(m_Scale);
	m_BG.SetBaseScreen( math::Vec2( GetGraphics()->GetScreenWidth()   , GetGraphics()->GetScreenHeight() ) );
	m_BG.Load("levels/map.lua");

	//math::Vec2 BGTileSize = m_BG.GetTileSize();
	//BGTileSize.x *= NewWScale;	
	//m_BG.SetTileSize( BGTileSize);
	
	int CountX = 3;
	int CountY = 2;

	/*
	m_BG.SetBackgroundTile(CountX, CountY, math::Vec2(1024.0f*GraphicsUtil::InvW, 1024.0f*GraphicsUtil::InvH));
	int index = 1;
	for(int y = 0; y < CountY; y++)
	{
		for (int x=0; x < CountX; x++)
		{
			sprintf(Buffer, "backgrounds/map_%03i.jpg", index);
			m_BG.SetTile(x, y, Buffer);
			index++;
		}
	}
	*/

	m_PreviousMousePos.x = m_PreviousMousePos.y = -2.0f;
	
	
	m_BG.SetOffset(math::Vec2(0.0f,1.0f));
	m_Offset = m_BG.GetOffset();
	
	m_CurrentWP = g_pApp->GetRMS()->GetInt(RMS_LASTLEVEL);
	if(m_CurrentWP == 0)
	{		
		m_CurrentWP = 1;	
		g_pApp->GetRMS()->SetOpen(m_CurrentWP,1);
	}

	

	background::BGWaypoint* pWP = m_BG.GetWaypointFromID(m_CurrentWP);    
	if(pWP)
	{
		m_BG.SetOffset( pWP->GetPos() - (math::Vec2(0.5f,0.5f) ) );
		m_Offset = m_BG.GetOffset();
	}
	//SetBGScale(1.35f);
	
	if(m_BG.GetWaypointFromID(101))
		m_BG.SetMax( m_BG.GetWaypointFromID(101)->GetPos() );
	
	MATSFXManager::Instance().Cache();
}


bool	MATMapState::PreRender()
{
	if(m_IsDebug)
	{
		dd::Manager&	DD = dd::Manager::Instance();
		std::vector<background::BGWaypoint*>* pWPs = m_BG.GetWaypoints();

		for(int i = 0; i < (int)pWPs->size(); i++)
		{
			background::BGWaypoint* pWP = (*pWPs)[i];
			const math::Vec2 WPPos = pWP->GetPos();
			DD.AddCircle2D( (WPPos - m_BG.GetOffset()) , WP_SIZE * m_Scale, ColorRGBA(255,0,0) );
		}

		FontManager::Instance().PrintCentered(math::Vec2(0.5f,0.5f), "%f, %f",  m_BG.GetOffset().x, m_BG.GetOffset().y);
	}

	std::vector<background::BGWaypoint*>* pWPs = m_BG.GetWaypoints();

	int LevelInt = 0;
	int StarCount = 0;
	math::Vec2 TempPos;
	math::Vec2 Dir;
	float Distance = 0.6f;
	for(int i = 0; i < (int)pWPs->size(); i++)
	{
		background::BGWaypoint* pWP = (*pWPs)[i];
		LevelInt = g_pApp->GetRMS()->IsOpen(pWP->GetID());

		// 0 is locked // 1 is 0 star.// 2 is 1 star. // 3 is 2 star // 4 is 2 star // 5 is 3 star
		StarCount = LevelInt - 2;

		if(LevelInt == 0 && i < 100)
		{
			const math::Vec2 WPPos = pWP->GetPos();
			m_SprLockedLevel.m_Pos = (WPPos - m_BG.GetOffset());			
			m_SprLockedLevel.RenderRotated();
		}

		
		if(LevelInt > 1)
		{
			for(int j = 0; j < StarCount; j++)
			{
				TempPos = pWP->GetPos() - m_BG.GetOffset();
				if( j == 0)
				{
					Distance = 1.0f;
					Dir = math::Facing::VectorFromAngle(180 - 55);
				}
				else if( j == 1)
				{
					Dir = math::Facing::VectorFromAngle(180);
					Distance = 0.8f;
				}
				else if( j == 2)
				{
					Distance = 1.0f;
					Dir = math::Facing::VectorFromAngle(180 + 55);
				}


				m_Stars[j].m_Color = ColorfRGBA(1.0f,1.0f,1.0f,1.0f);
				m_Stars[j].m_Size = m_StarSize;

				if( pWP->GetID() == m_AnimatedLevelIndex)
				{
					m_Stars[j].m_Color = ColorfRGBA(1.0f,1.0f,1.0f, m_AnimateInfo[j].m_Alpha );
					m_Stars[j].m_Size = m_StarSize * m_AnimateInfo[j].m_Scale;
				}
				
				m_Stars[j].m_Pos =  TempPos + ( Dir * m_SprLockedLevel.m_Size.y * Distance );
				m_Stars[j].RenderRotated();
				
			}
		}
		
	}

	

	return true;
}

bool	MATMapState::PostRender()
{
	
	

	if(m_ToSpawnParticle)
		m_Part2DIamHere.Render(math::Vec2(0,0),m_Scale);	
	return true;
}

void	MATMapState::AddGameState()
{
	gameflow::GameFlowManager&	Game = gameflow::GameFlowManager::Instance();
	Game.AddTransitionState("game", new GENGameState(m_pCamera,"ui/in_game.ui","",""));			
	_LOG(MSG_COMMENT ,"game GameState added!");
	m_IsFinishAddingGameState = true;
	//SetupTransition("buttton_1","game");
}


bool	MATMapState::Tick(float dt)
{
	

	for(int i = 0; i < 3; i++)
		m_AnimateInfo[i].Tick(dt);

	MATUIAnimator::Instance().Tick(dt);
	MATUIPopupAnimatorMgr::Instance().Tick(dt);
	m_Part2DIamHere.Tick(dt);	

	if(m_ToSpawnParticle)
	{
		AUDIO().PlayEvent("level_clicked");	
		m_CounterParticleRespawn.Tick(dt);
		if(m_CounterParticleRespawn.IsReady())
		{
			m_CounterParticleRespawn.Reset();
			m_Part2DIamHere.TriggerParticleSet(1 , m_PosParticle / m_Scale  );
		}
	}

	
	
	if (gameflow::GameFlowManager::Instance().InTransition()) 
		return gameflow::UIState::Tick(dt);

	ui::UIManager::Instance().SetVisible(true);	

	input::Mouse::BUTTON_STATE* pState =
	input::Mouse::Instance().GetButtonState(input::Mouse::BUTTON_LEFT);

	if(pState->IsButtonPressed()  )
		m_FirstMouseDownPos = input::Mouse::Instance().GetImmediatePos(); 

	if(pState->ButtonDown && m_CanMove && !m_BG.GetEditor() )
	{
		math::Vec2 Pos = input::Mouse::Instance().GetImmediatePos(); 
		if(m_PreviousMousePos.x > -1.0f && m_PreviousMousePos.y > -1.0f)
		{
			m_Offset -= ((Pos - m_PreviousMousePos) ); 				
			m_BG.SetOffset(m_Offset );
			
		}
		m_PreviousMousePos = Pos;	
		
	}
	else
	{
		m_PreviousMousePos.x =  m_PreviousMousePos.y = -2.0f;
	
	}

	if(!m_CanMove)
	{
		if(m_Delay > 0)
		{
			m_Delay-= dt;
		}
		else
		{

			m_Pos += (m_Dir * dt);

			math::Vec2 NewOffset = m_BG.GetOffset() + (m_Dir * dt * 1.5f);
			const math::Vec2 CurrentFocus = (m_Pos - m_BG.GetOffset()) * m_Scale;
			if( (CurrentFocus.y > 0.5f && m_Dir.y < 0 ) ||
				(CurrentFocus.y < 0.5f && m_Dir.y > 0 )
				)
				NewOffset.y = m_BG.GetOffset().y;

			if( (CurrentFocus.x < 0.5f && m_Dir.x < 0 )
				)
				NewOffset.x = m_BG.GetOffset().x;

			m_BG.SetOffset(NewOffset);

			math::Vec2 NewDir = m_PosTarget - m_Pos;
			NewDir.Normalize();

			bool Reach = false;
			if(NewDir.x > 0 && m_Dir.x <= 0)
				Reach = true;

			else if(NewDir.x < 0 && m_Dir.x >= 0)
				Reach = true;

			else if(NewDir.y > 0 && m_Dir.y <= 0)
				Reach = true;

			else if(NewDir.y < 0 && m_Dir.y >= 0)
				Reach = true;
			

			if(Reach)
			{
				m_PreviousMousePos.x =  m_PreviousMousePos.y = -2.0f;
				m_Offset = m_BG.GetOffset();							
				background::BGWaypoint* pWP = m_BG.GetWaypointFromID(m_CurrentWP);  
				{
					m_PosParticle = pWP->GetPos() -  m_BG.GetOffset() + math::Vec2(0, WP_SIZE * m_Scale * 0.5f);					
				}

				m_ToSpawnParticle = true;
				m_DelayGotoGame = 1.0f;				
				m_CanMove = true;
			}
		}
	}

	if(m_DelayGotoGame > 0)
	{
		m_DelayGotoGame -= dt;
		if( m_DelayGotoGame <= 0)
		{
			if(m_IsFinishAddingGameState == false)
			{
				AddGameState();
			}


			MATLevelManager::Instance().SetCurrentLevel(m_CurrentWP ,true);	
			gameflow::GameFlowManager::Instance().SetTransitionEffect("fade","game","",NULL);			
								
			
		}
	}
	
	MATSounds::Instance().Tick(dt);

	return gameflow::UIState::Tick(dt);
} 

bool		MATMapState::OnEnter()
{

	if(!m_IsLoaded)
	{
		LoadAsset();
		m_IsLoaded = true;
	}

#ifdef _CHEAT_UNLOCK_LEVELS
	// for testing
    
	for(int i = 0; i <= 100; i++)
	{
		if(g_pApp->GetRMS()->IsOpen(i) == 0)
			g_pApp->GetRMS()->SetOpen(i, num::RAND32::Instance().RandInt(3,5) );
	}
    
#endif

	background::BGWaypoint* pWP = m_BG.GetWaypointFromID(m_CurrentWP);    
	if(pWP)
	{
		m_BG.SetOffset( pWP->GetPos() - (math::Vec2(0.5f,0.5f) ) );
		m_Offset = m_BG.GetOffset();
	}

	m_CounterParticleRespawn.ForceReady();
	m_ToSpawnParticle = false;

	

	return UIState::OnEnter();
}

void		MATMapState::GoToNextWayPoint(int PreviousLevelStatus, int Stars)
{ 
	
	m_CanMove = false; 

	background::BGWaypoint* pWP = m_BG.GetWaypointFromID(m_CurrentWP);    
	if(pWP)
		m_Pos = pWP->GetPos();

	m_CurrentWP++; 
	pWP = m_BG.GetWaypointFromID(m_CurrentWP);;
	if(pWP)
		m_PosTarget = pWP->GetPos();

	
	m_Dir = m_PosTarget - m_Pos;
	m_Dir.Normalize();
	const float STAR_ANIMATION_DELAY = 0.5f;
	m_Delay = 0.5f;
	m_AnimatedLevelIndex = -1;
	if( Stars > PreviousLevelStatus - 2 )
	{
		m_AnimatedLevelIndex = m_CurrentWP - 1;
		m_Delay += (STAR_ANIMATION_DELAY * (Stars+1));
		for(int i = 0; i < Stars; i++)
		{
			m_AnimateInfo[i].Start(STAR_ANIMATION_DELAY * (i + 1));
		}
	}



	// 0 is locked // 1 is 0 star.// 2 is 1 star. // 3 is 2 star // 4 is 2 star // 5 is 3 star
	
}	


void		MATMapState::MouseButtonReleased(input::Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos)
{
	if(gameflow::GameFlowManager::Instance().GetTransition().IsRunning())
		return;

	if(m_DelayGotoGame > 0)
		return;

	if(m_BG.GetEditor())
	{
		UIState::MouseButtonReleased(pKey, Pos);
		return;
	}

	std::vector<int> m_ClickedWPs;
	math::Vec2 WPPos;
	std::vector<background::BGWaypoint*>* pWPs = m_BG.GetWaypoints();
	if( (Pos - m_FirstMouseDownPos ).Magnitude() < 0.025f)
	{
		// valid mouse click ( not dragging )		
		for(int i = 0; i < (int)pWPs->size(); i++)
		{
			background::BGWaypoint* pWP = (*pWPs)[i];
			WPPos = (pWP->GetPos() - m_BG.GetOffset());
			if( (Pos - WPPos).Magnitude() < WP_SIZE * m_Scale  )
			{
				m_ClickedWPs.push_back(i);
			}
		}
	
	}

	int m_NearestWP = -1;
	float NearestDist = 9999.0f;
	float Dist;
	for(int i = 0; i < (int)m_ClickedWPs.size(); i++)
	{
		background::BGWaypoint* pWP = (*pWPs)[i];
		WPPos = (pWP->GetPos() - m_BG.GetOffset());		
		Dist = (Pos - WPPos).Magnitude();
		if(Dist < NearestDist)
		{
			m_NearestWP = m_ClickedWPs[i];
			NearestDist = Dist;
		}

	}


	if(m_NearestWP >=0)
	{
		background::BGWaypoint* pWP = (*pWPs)[m_NearestWP];
		WPPos = (pWP->GetPos() - m_BG.GetOffset());	
		if(g_pApp->GetRMS()->IsOpen(pWP->GetID())> 0)
		{
			m_PosParticle = WPPos + math::Vec2(0, WP_SIZE * m_Scale * 0.5f);
			m_ToSpawnParticle = true;
			m_DelayGotoGame = 1.0f;
								
			m_CurrentWP = pWP->GetID();
			return;
		}
	}
	
}

void	MATMapState::AnimatedStamp::Tick(float dt)
{
	
	if(m_Delay > 0)
	{
		m_Delay -= dt;
		return;
	}
	
	if(m_IsFinish)
		return;
	m_Scale -= dt * 8.0f;
	m_Alpha += dt * 4.0f;
	if(m_Alpha > 1.0f)
		m_Alpha = 1.0f;

	if(m_Scale <= 1.0f)
	{
		m_Scale = 1.0f;	
		m_IsFinish = true;				
		MATSounds::Instance().PlaySoundSFX(MATSounds::SFX_RATINGSTAR_APPEAR);
	}

}

void	MATMapState::AnimatedStamp::Start(float Delay)
{
	m_IsFinish = false;
	m_Scale = 4.0f;
	m_Alpha = 0.0f;
	m_Delay = Delay;
}

void	MATMapState::KeyPressed(input::Keyboard::KEY* pKey)
{
	if(gameflow::GameFlowManager::Instance().GetTransition().IsRunning())
		return;

	if(pKey->KeyID == KEY_ESCAPE)
	{
		gameflow::GameFlowManager::Instance().SetTransitionEffect("fade","mainmenu","",NULL);
	}
	else if(pKey->KeyID == KEY_D)
		m_IsDebug = !m_IsDebug;

	else
		UIState::KeyPressed(pKey);
}
